/**
 * 
 */
package wosaic.ui;

import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Point;

import javax.swing.JComponent;

import wosaic.utilities.MosaicEvent;
import wosaic.utilities.MosaicListener;
import wosaic.utilities.Pixel;

/**
 * A UI element that will display a visual representation of the underlying
 * Mosaic.
 * 
 * @author scott
 */
public class MosaicPane extends JComponent implements MosaicListener {

	/**
	 * A UI component holding the visual representation of one tile
	 * 
	 * @author scott
	 */
	public class MosaicPaneTile extends JComponent {

		/**
		 * serialVersionID generated by Eclipse.
		 */
		private static final long serialVersionUID = -1548498823961157782L;

		/**
		 * The image representing this individual Tile in the mosaic
		 */
		protected Pixel thePixel = null;

		/**
		 * Default constructor.
		 */
		public MosaicPaneTile() {
			super();

			// Decrease painting cost by "agreeing" to paint entire rectangle.
			setOpaque(true);
		}

		/**
		 * Paint the current image onto our graphics
		 * 
		 * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
		 */
		@Override
		public void paintComponent(final Graphics grx) {
			if (thePixel != null)
				// grx.drawImage(theImage, 0, 0, getWidth(), getHeight(), this);
				grx.drawImage(thePixel.getScaledImage(getWidth(), getHeight()),
						0, 0, null);
			else
				grx.clearRect(0, 0, getWidth(), getHeight());

			// FIXME: Should the call to super come before or after?
			super.paintComponent(grx);
		}

		/**
		 * Change the Image that represents this tile
		 * 
		 * @param pixel
		 *            The new Pixel
		 */
		public void UpdateTilePixel(final Pixel pixel) {
			thePixel = pixel;
			// TODO: Use the parametrized version of repaint to only paint the
			// the bounding rectangle.
			repaint();
		}
	}

	/**
	 * serialVersionID generated by Eclipse.
	 */
	private static final long serialVersionUID = -8587141268793103714L;

	/**
	 * A grid representing each tile of the Mosaic
	 */
	protected MosaicPaneTile[][] TileGrid;

	/**
	 * Default constructor.
	 */
	public MosaicPane() {
		super();

		// This makes all painting happen off-screen first, and then is copied
		// to the screen. Should help with the large number of updates.
		setDoubleBuffered(true);

	}

	/**
	 * Default constructor.
	 * 
	 * @param rows
	 *            The Mosaic resolution height
	 * @param cols
	 *            The Mosaic resolution width
	 */
	public MosaicPane(final int rows, final int cols) {
		super();

		// This makes all painting happen off-screen first, and then is copied
		// to the screen. Should help with the large number of updates.
		setDoubleBuffered(true);

		setGrid(rows, cols);
	}

	/**
	 * Remove all of the tiles in our grid. This is a simple wrapper around
	 * JPanel.removeAll().
	 */
	public void clearGrid() {
		removeAll();
		TileGrid = null;
	}

	/**
	 * Update our display based on the received event
	 * 
	 * @see wosaic.utilities.MosaicListener#mosaicUpdated(wosaic.utilities.MosaicEvent)
	 */
	public void mosaicUpdated(final MosaicEvent e) {

		for (final Point coord : e.Coords)
			UpdateTile(coord.x, coord.y, e.UpdatedPixel);

	}

	/**
	 * Create new tiles for each pixel in the panel
	 * 
	 * @param rows
	 *            number of pixels in each column
	 * @param cols
	 *            number of pixels in each row
	 */
	public void setGrid(final int rows, final int cols) {
		if (TileGrid != null) {
			clearGrid();
			System.gc();
		}

		// Setup our layout
		setLayout(new GridLayout(rows, cols));

		// initialize our tiles and add them to the grid
		TileGrid = new MosaicPaneTile[rows][cols];
		for (int h = 0; h < rows; h++)
			for (int w = 0; w < cols; w++) {
				TileGrid[h][w] = new MosaicPaneTile();
				add(TileGrid[h][w]);
			}
		invalidate();
	}

	/**
	 * Update the Image on a specific tile
	 * 
	 * @param x
	 *            The row of the tile to update
	 * @param y
	 *            The column of the tile to update
	 * @param pix
	 *            The new pixel
	 */
	public void UpdateTile(final int x, final int y, final Pixel pix) {
		TileGrid[x][y].UpdateTilePixel(pix);
	}
}
